
package com.huaweicloud.haydn.delivercore.agent.service;

import static com.huaweicloud.haydn.delivercore.agent.config.Constants.ACCESS_CODE;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.AUTO_REPORT;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_HOSTS;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_LINKNAME;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_PASSWORD;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_PORT;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_SELECTED;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_TYPE;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.DB_USERNAME;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.EMPTY_FIELD;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.EMPTY_TABLE;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.FILETYPE_PROPERTIES;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.HAYDN_URL;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.PROJECT_ID;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.SCHEMA_SELECTED;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.SYS_URL;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TABLE_SELECTED;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_DB;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_EMPTY;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_FIELD;
import static com.huaweicloud.haydn.delivercore.agent.config.Constants.TYPE_TABLE;

import com.alibaba.excel.util.StringUtils;
import com.huaweicloud.haydn.delivercore.agent.common.exception.BusinessException;
import com.huaweicloud.haydn.delivercore.agent.config.DatabaseContext;
import com.huaweicloud.haydn.delivercore.agent.factory.DatalinkPoolFactory;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.ConsistencyMonitoringService;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.DataExplorationService;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.DatabaseUtil;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.ImplementMonitoringService;
import com.huaweicloud.haydn.delivercore.agent.service.metadata.impl.AgentExecUtil;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.ExecutorService;

import javax.annotation.PostConstruct;

/**
 * @since 2022-10-08
 */
@Component
@Slf4j
@PropertySource("file:application.properties")
public class ScheduledTask {

    @Autowired
    private AgentExecUtil excelUtil;

    @Autowired
    DatabaseContext databaseContext;

    @Autowired
    private DataExplorationService dataExplorationService;

    @Autowired
    private ImplementMonitoringService implementMonitoringService;

    @Autowired
    private ConsistencyMonitoringService consistencyMonitoringService;

    private static final ExecutorService EXECUTOR_SERVICE;

    static {
        EXECUTOR_SERVICE = DatalinkPoolFactory.defaultExecutorService();
    }

    @Scheduled(cron = "${db.cron}")
    @SneakyThrows
    public void genData() {
        log.info("SCHEDULED gen db now");
        genDataType(TYPE_DB);
    }

    @Scheduled(cron = "${table.cron}")
    @SneakyThrows
    public void genTableData() {
        log.info("SCHEDULED gen table now");
        genDataType(TYPE_TABLE);
    }

    @Scheduled(cron = "${field.cron}")
    @SneakyThrows
    public void genFieldData() {
        log.info("SCHEDULED gen field now");
        genDataType(TYPE_FIELD);
    }

    @Scheduled(cron = "${tablenaming.cron}")
    @SneakyThrows
    public void genTableNamingData() {
        log.info("SCHEDULED gen table naming monitor result now");
        implementMonitoringService.genTableNamingData();
    }

    @Scheduled(cron = "${tablefield.cron}")
    @SneakyThrows
    public void genAdditionalFieldData() {
        log.info("SCHEDULED gen additional field monitor result now");
        implementMonitoringService.genAdditionalFieldData();
    }

    @Scheduled(cron = "${report.cron}")
    @SneakyThrows
    public void reportData() {
        log.info("report now");
        InputStream in = null;
        try {
            Properties dataSourceConfig = new Properties();
            File file = new File(System.getProperty(SYS_URL) + File.separator + "application." + FILETYPE_PROPERTIES);
            in = new FileInputStream(file);
            dataSourceConfig.load(in);
            String autoReport = dataSourceConfig.getProperty(AUTO_REPORT);
            String accessCode = dataSourceConfig.getProperty(ACCESS_CODE);
            String projectId = dataSourceConfig.getProperty(PROJECT_ID);
            String url = dataSourceConfig.getProperty(HAYDN_URL);
            if ("true".equals(autoReport)) {
                dataExplorationService.parseAndUpload(accessCode, projectId, url);
            }
        } catch (IOException | BusinessException e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    public void genDataType(String type) throws IOException {
        log.info("gen data: " + type);
        InputStream in = null;
        try {
            Properties dataSourceConfig = new Properties();
            File file = new File(System.getProperty(SYS_URL) + File.separator + "application." + FILETYPE_PROPERTIES);
            in = new FileInputStream(file);
            dataSourceConfig.load(in);
            log.info("it is time");
            for (int i = 0; i < 100; i++) {
                if (!read(dataSourceConfig, i + 1)) {
                    log.info("break at i = " + i + ", idx = " + i + 1);
                    break;
                }
                genDataInPool(i + 1, dataSourceConfig, type);
            }

        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    private void genDataInPool(int idx, Properties dataSourceConfig, String type) {
        log.info(idx + "");
        String datalinkName = dataSourceConfig.getProperty(DB_LINKNAME + idx);
        String hosts = dataSourceConfig.getProperty(DB_HOSTS + idx);
        String port = dataSourceConfig.getProperty(DB_PORT + idx);
        String userName = dataSourceConfig.getProperty(DB_USERNAME + idx);
        String password = dataSourceConfig.getProperty(DB_PASSWORD + idx);
        String dbType = dataSourceConfig.getProperty(DB_TYPE + idx);
        DatabaseUtil dbUtil = databaseContext.getStrategy(dbType);
        String databaseName;
        if ("mysql".equals(dbType)) {
            databaseName = "information_schema";
        } else {
            databaseName = dataSourceConfig.getProperty(DB_SELECTED + idx);
        }
        dbUtil.init(hosts, port, databaseName, userName, password, dbType, "", "");
        String dbSelected = Optional.ofNullable(dataSourceConfig.getProperty(DB_SELECTED + idx)).orElse("");
        String schemaSelected = Optional.ofNullable(dataSourceConfig.getProperty(SCHEMA_SELECTED + idx)).orElse("");
        String tableSelected = Optional.ofNullable(dataSourceConfig.getProperty(TABLE_SELECTED + idx)).orElse("");
        String idxStr = idx + "";
        if (TYPE_DB.equals(type)) {
            excelUtil.genDbExcelDto(dbUtil, datalinkName, dbSelected, schemaSelected, idxStr);
        } else if (TYPE_TABLE.equals(type)) {
            excelUtil.genTableExcelDto(dbUtil, datalinkName, dbSelected, schemaSelected, tableSelected, idxStr);
        } else if (TYPE_FIELD.equals(type)) {
            excelUtil.genFieldExcelDto(dbUtil, datalinkName, dbSelected, schemaSelected, tableSelected, idxStr);
        } else if (TYPE_EMPTY.equals(type) && "oracle".equals(dbType)) {
            Map<String, List<String>> fieldTableMap = new HashMap<>();
            for (int i = 1; i < 6; i++) {
                if (dataSourceConfig.getProperty(EMPTY_TABLE + idx + "." + i) == null
                    || dataSourceConfig.getProperty(EMPTY_FIELD + idx + "." + i) == null) {
                    break;
                }
                String emptyTable = dataSourceConfig.getProperty(EMPTY_TABLE + idx + "." + i);
                String emptyFields = dataSourceConfig.getProperty(EMPTY_FIELD + idx + "." + i);
                String[] fieldArr = emptyFields.split(",");
                List<String> fields = Arrays.asList(fieldArr);
                fieldTableMap.put(emptyTable, fields);
            }
            excelUtil.genEmptyRateExcelDto(dbUtil, datalinkName, dbSelected, schemaSelected, tableSelected, idxStr,
                fieldTableMap);
        } else {
            log.info("nothing happened");
        }
    }

    private boolean read(Properties dataSourceConfig, int i) {
        boolean proBase = StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_LINKNAME + i))
            || StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_HOSTS + i))
            || StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_PORT + i));

        boolean proSuper = StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_USERNAME + i))
            || StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_PASSWORD + i))
            || StringUtils.isNotBlank(dataSourceConfig.getProperty(DB_TYPE + i));

        return proBase || proSuper;
    }

    @Scheduled(cron = "${monitor.cron}")
    @SneakyThrows
    public void reportMonitorData() {
        log.info("report monitor data now");
        InputStream in = null;
        try {
            Properties dataSourceConfig = new Properties();
            File file = new File(System.getProperty(SYS_URL) + File.separator + "application." + FILETYPE_PROPERTIES);
            in = new FileInputStream(file);
            dataSourceConfig.load(in);
            String accessCode = dataSourceConfig.getProperty("monitor.access.code");
            String projectId = dataSourceConfig.getProperty("monitor.project.id");
            String url = dataSourceConfig.getProperty("monitor.haydn.url");
            implementMonitoringService.parseAndUpload(accessCode, projectId, url);
            log.info("report monitor data succeed!");
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    @Scheduled(cron = "${consistency.tablefield.cron}")
    @SneakyThrows
    public void genTableFieldData() {
        log.info("SCHEDULED generate table field monitor consistency result now");
        consistencyMonitoringService.genTableFieldData();
    }

    @Scheduled(cron = "${consistency.tablerows.cron}")
    @SneakyThrows
    @PostConstruct
    public void genTableRowsData() {
        log.info("SCHEDULED generate table rows monitor consistency result now");
        consistencyMonitoringService.genTableRowsData();
    }

    @Scheduled(cron = "${consistency.tablecontents.cron}")
    @PostConstruct
    @SneakyThrows
    public void genTableContentsData() {
        log.info("SCHEDULED generate table contents monitor consistency result now");
        consistencyMonitoringService.genTableContentData();
    }

    @Scheduled(cron = "${consistency.monitor.cron}")
    @SneakyThrows
    public void reportMonitorConsistencyData() {
        consistencyMonitoringService.reportMonitorConsistencyData();
    }
}
